#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/compat.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/input.h>
#include <linux/of_irq.h>
#include <linux/ioctl.h>
#include <linux/wakelock.h>

#include "mpu6050_register.h"
#include "mpu6050.h"


#define MPU6050_NAME "atk_mpu6050"
#define MAX_BUFFER_SIZE 256
#define LED_STATUS_OFF 0x10
#define LED_STATUS_ON  0x11

static DECLARE_WAIT_QUEUE_HEAD(mpu_device_wait_queue);
static void timer_function(struct timer_list *unused);

struct mpu_cmd{
    u8 reg;
    u8 buf[32];
    u8 len;
};

struct mpu_cmd *mpucmd;
struct mpu6050_dev *mpu6050dev;
int g_cmd_reg;
static unsigned int irq;
//静态定义结构体变量并且初始化function，expires，data成员
static DEFINE_TIMER(key_timer,timer_function);

static int atk_open(struct inode *node, struct file *file)
{
    struct mpu6050_dev *dev = container_of(file->private_data, struct mpu6050_dev, mpu_misc_device);
    file->private_data = dev;
    printk(KERN_ALERT"%s: open\n",__func__);
    return 0;
}

static int atk_release(struct inode *node, struct file *file)
{
    return 0;
}

static ssize_t atk_read(struct file *filp, char *buf, size_t len, loff_t *offset)
{
    int ret = -1;
    struct mpu6050_dev *dev = filp->private_data;

    printk(KERN_ALERT" %s: len=%ld\n",__func__,len);

    if(copy_from_user(mpucmd, buf, len)){
    }
    g_cmd_reg = mpucmd->reg;
    regmap_raw_read(dev->regmap, g_cmd_reg, mpucmd->buf, mpucmd->len);
    mpucmd->buf[mpucmd->len++]='\n';
    mpucmd->buf[mpucmd->len]='\0';

    printk(KERN_ALERT"%s: reg=0x%x,len=%d, tmp=%s",__func__, g_cmd_reg,mpucmd->len, mpucmd->buf);
    if(copy_to_user(buf, mpucmd, len)){
        pr_err("%s: failed to copy to user space \n",__func__);
        return -EFAULT;
    }
    return ret;
}

static ssize_t atk_write(struct file *filp, const char __user *buf, size_t len, loff_t *offt)
{
    int ret = -1;
    struct mpu6050_dev *dev = filp->private_data;


    if(copy_from_user(mpucmd, buf, len)){
         pr_err("%s: failed to copy from user space \n", __func__);
         return -EFAULT;
    }

    g_cmd_reg = mpucmd->reg;
    ret = regmap_raw_write(dev->regmap, g_cmd_reg, mpucmd->buf, mpucmd->len);
    printk(KERN_ALERT"%s: len = %ld, g_cmd_reg=0x%x, tmp=%s\n",__func__, len, g_cmd_reg, mpucmd->buf);

    return ret;
}


static long atk_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
    long ret = 0;
    printk(KERN_ALERT"cmd = %x, arg=%ld \n", cmd, arg);
    switch(cmd){
        case IOCSETREGCMD:
            ret = copy_from_user(&g_cmd_reg, (unsigned char *)arg, sizeof(unsigned char));
            printk(KERN_ALERT"%s: g_cmd_reg = 0x%x\n",__func__, g_cmd_reg);
            break;
        case SLEEP:
            mpu6050dev->dev_irq = 0;
            wait_event_interruptible(mpu6050dev->gyro_wait, mpu6050dev->dev_irq != 0);

            break;
        case WAKEUP:
            mpu6050dev->dev_irq = 1;
            wake_up(&mpu6050dev->gyro_wait);
            break;
        case TIMEOUT_WAKELOCK:
            wake_lock_timeout(&mpu6050dev->wake_lock, msecs_to_jiffies(2000));
            break;
        case LED_ON:
            printk(KERN_ALERT"%s: ioctl led on \n",__func__);
            if(mpu6050dev->led_status == LED_ON){
                break;
            }
            gpio_set_value(mpu6050dev->led_gpio, 1);
            mpu6050dev->led_status = LED_STATUS_ON;
            break;
        case LED_OFF:
            printk(KERN_ALERT"%s: ioctl led off \n",__func__);
            if(mpu6050dev->led_status == LED_OFF){
                break;
            }
            gpio_set_value(mpu6050dev->led_gpio, 0);
            mpu6050dev->led_status = LED_STATUS_OFF;
            break;
    }
    return ret;
}

/**
 * @brief 字符设备
 *
 */
struct file_operations fops = {
    .open = atk_open,
    .release = atk_release,
    .read = atk_read,
    .write = atk_write,
    .unlocked_ioctl = atk_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = atk_ioctl,
#endif

};

static void timer_function(struct timer_list *unused)
{
	printk("This is timer function!\n");
    printk(KERN_ALERT"%s: start, led status:0x%x",__func__,mpu6050dev->led_status);
    mpu6050dev->dev_irq = 1;
    wake_up(&mpu6050dev->gyro_wait);
    printk(KERN_ALERT"%s: irq gpio =%d",__func__,gpio_get_value(mpu6050dev->irq_gpio));
    if(gpio_get_value(mpu6050dev->irq_gpio)){
        if(mpu6050dev->led_status == LED_STATUS_ON){
            printk(KERN_ALERT"current led status:LED_OFF \n");
            gpio_set_value(mpu6050dev->led_gpio, 0);
            mpu6050dev->led_status = LED_STATUS_OFF;
        }else{
            printk(KERN_ALERT"current led status:LED_ON \n");
            gpio_set_value(mpu6050dev->led_gpio, 1);
            mpu6050dev->led_status = LED_STATUS_ON;
        }
    }
}



/* work handler */
static void mpu6050_work(struct work_struct *work)
{
    //启动定时器 jiffies是全局变量，用来表示当前系统时间 1S=1000个滴答数
	mod_timer(&key_timer,jiffies + 5*HZ/100); //设置50ms超时 1HZ=1S
}

static irqreturn_t mpu6050_irq_handler(int irq, void *dev_id)
{
    struct mpu6050_dev *dev = dev_id;
    printk(KERN_ALERT"%s: interrupt work...\n",__func__);
    queue_work(dev->wq, &dev->work);

    return IRQ_HANDLED;
}

static int destroy_dev(void)
{
    wake_lock_destroy(&mpu6050dev->wake_lock);
    destroy_workqueue(mpu6050dev->wq);
    free_irq(irq, mpu6050dev);
    gpio_free(mpu6050dev->irq_gpio);
    del_timer(&key_timer);
    return 0;
}

static const struct regmap_config mpu6050_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = 0xff,
//   .cache_type = REGCACHE_RBTREE, 配置会导致收据读取失败，读出收据不变
};

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = -1;
    unsigned long irq_flags;

    printk(KERN_ALERT" mpu6050 probe start\n");

    mpu6050dev = kzalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);
    if(!mpu6050dev){
        printk(KERN_ALERT" Failed to alloc mpu6050dev \n");
        return -ENOMEM;
    }

    mpu6050dev->client = client;
    mpu6050dev->regmap = devm_regmap_init_i2c(client, &mpu6050_regmap_config);
    if(IS_ERR(mpu6050dev->regmap))
        return PTR_ERR(mpu6050dev->regmap);

    mpucmd = kzalloc(sizeof(struct mpu_cmd), GFP_DMA | GFP_KERNEL);

    //杂项设备配置
    mpu6050dev->mpu_misc_device.minor = MISC_DYNAMIC_MINOR;
    mpu6050dev->mpu_misc_device.name = "atkmpu6050";
    mpu6050dev->mpu_misc_device.fops = &fops;
    mpu6050dev->buf = kzalloc(MAX_BUFFER_SIZE, GFP_DMA | GFP_KERNEL);

    ret = misc_register(&mpu6050dev->mpu_misc_device);
    if (ret)
    {
        printk(KERN_ALERT" MPU6050 register failed \n");
        goto err_free;
    }

    key_timer.expires = jiffies + msecs_to_jiffies(20);
	//注册定时器
	add_timer(&key_timer);

    //初始化等待队列
    init_waitqueue_head(&mpu6050dev->gyro_wait);
    //初始化唤醒锁
    wake_lock_init(&mpu6050dev->wake_lock, WAKE_LOCK_SUSPEND, "mpu6050_bat_lock");

    //初始化线程调度
    INIT_WORK(&mpu6050dev->work, mpu6050_work);
    mpu6050dev->wq = create_singlethread_workqueue("mpu6050_workqueue");
    if (!mpu6050dev->wq) {
        ret = -ENOMEM;
        goto err_dev_wq;
    }

    mpu6050dev->node = of_find_compatible_node(NULL, NULL, "ATK,mpu6050");
    if (mpu6050dev->node == NULL){
        printk("gpioled node not find!\r\n");
        goto err_free;
     }

    //mpu6050dev->node = client->dev.of_node;

    mpu6050dev->irq_gpio = of_get_named_gpio_flags(mpu6050dev->node, "irq-gpio", 0, (enum of_gpio_flags *)&irq_flags);
    if(mpu6050dev->irq_gpio < 0){
        printk("can't get irq-gpio conf");
        //goto err_free;
        return -EINVAL;
    }

    mpu6050dev->led_gpio = of_get_named_gpio(mpu6050dev->node, "led-gpio", 0);
    if(mpu6050dev->led_gpio < 0){
        printk("can't get led-gpio conf");
        //goto err_free;
        return -EINVAL;
    }
    gpio_request(mpu6050dev->led_gpio, "led0");
    gpio_direction_output(mpu6050dev->led_gpio, 0); /*设置为输出，默认低电平 */

    //申请gpio
    gpio_request(mpu6050dev->irq_gpio, "irq");
    //设置gpio为输入模式
    gpio_direction_input(mpu6050dev->irq_gpio);
    //动态获取中断号
    irq = gpio_to_irq(mpu6050dev->irq_gpio);
    //初始化中断
    ret = request_irq(irq, mpu6050_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mpu6050,eint", mpu6050dev);
    if(ret < 0){
        goto err_dev_wq;
    }

    printk(KERN_ALERT" mpu6050 probe end\n");

    return 0;
err_dev_wq:
    destroy_dev();
err_free:
    kfree(mpu6050dev);
    return ret;
}


static int mpu6050_remove(struct i2c_client *client)
{
    misc_deregister(&mpu6050dev->mpu_misc_device);
    destroy_dev();
    free_irq(irq, mpu6050dev);
    gpio_free(mpu6050dev->irq_gpio);
    kfree(mpu6050dev);
    del_timer(&key_timer);
    return 0;
}


static const struct of_device_id mpu6050_match[] = {
    { .compatible = "ATK,mpu6050" },
    {},
};

struct i2c_device_id mpu6050_id[]={
    {"mpu6050", 0},
};

struct i2c_driver mpu6050_driver={
    .driver = {
        .name = "mpu6050",
        .owner = THIS_MODULE,
        .of_match_table = mpu6050_match,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .id_table = mpu6050_id,
};

static int __init i2c_mpu6050_init(void)
{
    int ret=-1;
    printk(KERN_ALERT"insmod loading mpu6050 \n");
    ret = i2c_add_driver(&mpu6050_driver);
    if(ret){
        printk(KERN_ALERT"add mpu6050 driver fail!!!\n");
        return ret;
    }
    return ret;
}

static void __exit i2c_mpu6050_exit(void)
{
    printk(KERN_ALERT"rmmod mpu6050 driver \n");
    i2c_del_driver(&mpu6050_driver);
}

module_init(i2c_mpu6050_init);
module_exit(i2c_mpu6050_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dylan");
MODULE_DESCRIPTION("ATK mpu6050 module for test");
